
#include "stdafx.h"
#include "generator_cache.h"

using namespace boost::python;
using namespace std;

struct GeneratorInfo {
   string command;
   string file;
   int    line;

   string errorMsg;
   int    errorsCount;

   GeneratorInfo(void)
      : line(0)
      , errorsCount(0)
   {}

   void init(const GENERATOR_ERROR_INFO & info) {
      command  = info.command.CStr();
      file     = info.file.CStr();
      line     = info.line;
   }

   CLASS_WRAPPER_BEGIN(GeneratorInfo)
      class_<GeneratorInfo> ("Info")
         .def_readonly("command",      &GeneratorInfo::command)
         .def_readonly("file",         &GeneratorInfo::file)
         .def_readonly("line",         &GeneratorInfo::line)
         .def_readwrite("errorMsg",    &GeneratorInfo::errorMsg)
         .def_readwrite("errorsCount", &GeneratorInfo::errorsCount)
      ;
   CLASS_WRAPPER_END()
};

struct PYTHON_CALLBACK : GENERATOR_CALLBACK {
   PYTHON_CALLBACK(object callback)
      : callback(callback)
   {}

   object callback;

   virtual bool Begin(const GENERATOR_ERROR_INFO & errorInfo) {
      GeneratorInfo * infoVal = NULL;
      object info = GeneratorInfo::New(&infoVal);
      infoVal->init(errorInfo);

      if (PyObject_HasAttrString(callback.ptr(), "begin")) {
         callback.attr("begin")(info);
      }
      
      errorInfo.errorMsg      = infoVal->errorMsg.c_str();
      errorInfo.errorsCount   = infoVal->errorsCount;

      return errorInfo.errorsCount == 0;
   }

   virtual bool Input(const dsSTRING & paramsStr, const dsSTRING & text, const GENERATOR_ERROR_INFO & errorInfo) {
      GeneratorInfo * infoVal = NULL;
      object info = GeneratorInfo::New(&infoVal);
      infoVal->init(errorInfo);

      dsVECTOR<dsSTRING> paramsVec;
      genParseArgs(paramsStr, paramsVec);
      
      list params;
      for (int i = 0; i < paramsVec.Length(); ++i) {
         params.append(str(paramsVec[i].CStr()));
      }

      callback.attr("input")(params, text.CStr(), info);
      
      errorInfo.errorMsg      = infoVal->errorMsg.c_str();
      errorInfo.errorsCount   = infoVal->errorsCount;

      return errorInfo.errorsCount == 0;
   }

   virtual bool Output(const dsSTRING & paramsStr, dsSTRING & res, const GENERATOR_ERROR_INFO & errorInfo) {
      GeneratorInfo * infoVal = NULL;
      object info = GeneratorInfo::New(&infoVal);
      infoVal->init(errorInfo);

      dsVECTOR<dsSTRING> paramsVec;
      genParseArgs(paramsStr, paramsVec);
      
      list params;
      for (int i = 0; i < paramsVec.Length(); ++i) {
         params.append(str(paramsVec[i].CStr()));
      }

      object resObj = callback.attr("output")(params, info);

      errorInfo.errorMsg      = infoVal->errorMsg.c_str();
      errorInfo.errorsCount   = infoVal->errorsCount;

      if (errorInfo.errorsCount != 0) {
         return false;
      }

      res = (const char *) extract<const char *> (resObj);
      return true;
   }

   virtual bool End(const GENERATOR_ERROR_INFO & errorInfo) {
      GeneratorInfo * infoVal = NULL;
      object info = GeneratorInfo::New(&infoVal);
      infoVal->init(errorInfo);

      if (PyObject_HasAttrString(callback.ptr(), "end")) {
         callback.attr("end")(info);
      }
      
      errorInfo.errorMsg      = infoVal->errorMsg.c_str();
      errorInfo.errorsCount   = infoVal->errorsCount;

      return errorInfo.errorsCount == 0;
   }
};

struct Searcher {
   GENERATOR_CACHE   cache;

   int errorsCount;
   string errorMsg;

   Searcher(void)
      : errorsCount(0)
   {}

   void saveCache(string filename) {
      cache.Save(filename.c_str());
   }

   void loadCache(string filename) {
      cache.Load(filename.c_str());
   }

   bool search(list searchPath, list exclude, list extensions) {
      errorsCount = 0;
      errorMsg.clear();

      cache.ResetExtensions();
      for (size_t k = 0, length = len(extensions); k < length; ++k) {
         const char * val = extract<const char *> (extensions[k]);
         cache.AddExtension(val);
      }

      dsVECTOR<dsSTRING> excl;
      for (size_t k = 0, length = len(exclude); k < length; ++k) {
         const char * val = extract<const char *> (exclude[k]);
         excl += val;
      }

      for (size_t k = 0, length = len(searchPath); k < length; ++k) {
         const char * val = extract<const char *> (searchPath[k]);
         if (!cache.Search(val, excl)) {
            errorsCount = cache.errorsCount;
            errorMsg    = cache.errorMsg.CStr();
            return false;
         }
      }

      return true;
   }

   bool execute(void) {
      errorsCount = 0;
      errorMsg.clear();

      if (!cache.Execute()) {
         errorsCount = cache.errorsCount;
         errorMsg = cache.errorMsg.CStr();

         return false;
      }

      return true;
   }

   void addGenerator(string name, object gen) {
      PYTHON_CALLBACK * callback = new PYTHON_CALLBACK(gen);

      cache.AddGenerator(name.c_str(), callback);
   }
};

BOOST_PYTHON_MODULE(AutoTagSearcher)
{
   class_<Searcher> ("Searcher")
      .def("saveCache",                &Searcher::saveCache)
      .def("loadCache",                &Searcher::loadCache)
      .def("addGenerator",             &Searcher::addGenerator)
      .def("search",                   &Searcher::search)
      .def("execute",                  &Searcher::execute)
      .def_readonly("errorsCount",     &Searcher::errorsCount)
      .def_readonly("errorMsg",        &Searcher::errorMsg)
   ;

   GeneratorInfo::Type();
}